﻿using System;
using UnityEngine;

namespace Z1029.NoteGlobalView
{
    public class Note : MonoBehaviour, IComparable<Note>
    {
        [HideInInspector]
        public NoteType type;
        public Direction direction;
        public NoteData noteData;
        public int noteSoundID;

        [Tooltip("HL Sprite")]
        public Sprite HighLight;
        public Sprite[] notes;
        public SpriteRenderer thisRenderer;
        public JudgementLine judgeline;
        public bool isRegistered;

        public virtual void Awake()
        {
            thisRenderer = GetComponent<SpriteRenderer>();
        }

        public virtual void OnNoteStart()
        {
        }

        public virtual void Start()
        {
            isRegistered = false;
            transform.parent = judgeline.transform;
            noteData.time = Utils.GetTimeFromBeatTime(noteData.beatTime, judgeline.bpm);
            noteData.holdTime = Utils.GetTimeFromBeatTime(noteData.beatHoldTime, judgeline.bpm);
            thisRenderer.color = Color.white;
            transform.localScale = new Vector3(LevelController.noteSize, LevelController.noteSize, 1f);
            transform.localEulerAngles = Vector3.zero;
            OnNoteStart();
            transform.localPosition = new Vector3(noteData.positionX, 0, 0);
            SetNotePosition();
        }

        public void SetNoteHighLight()
        {
            if (HLNote(noteData.beatTime))
            {
                thisRenderer.sprite = HighLight;
            }
        }

        public virtual float CalculateNotePosY()
        {
            float num = CalculateDistance();
            if (direction == Direction.Below)
            {
                num = -num;
            }
            float realY = transform.localPosition.y + (num - transform.localPosition.y);
            return realY;
        }

        public virtual float CalculateDistance()
        {
            return (float)(noteData.speed * judgeline.GetSpeedDistance(noteData.time));
        }

        public virtual void SetNotePosition()
        {
            transform.localPosition = new Vector3(noteData.positionX, CalculateNotePosY(), 0f);
        }

        public virtual void ChangeToMissColor()
        {
            thisRenderer.color = new Color(1f, 1f, 1f, 0.4f);
        }


        public virtual void Update()
        {
            noteData.time = Utils.GetTimeFromBeatTime(noteData.beatTime, judgeline.bpm);
            noteData.holdTime = Utils.GetTimeFromBeatTime(noteData.beatHoldTime, judgeline.bpm);
            thisRenderer.sprite = notes[(int)noteData.type];
            //noteData.positionX = transform.localPosition.x;
            if (noteData.type != NoteType.Hold)
            {
                SetNotePosition();
                OnNoteUpdate();

                transform.localScale = new Vector3(LevelController.noteSize, LevelController.noteSize, 1f);
                transform.localEulerAngles = Vector3.zero;
                if (CalculateDistance()>=0&&LevelController.nowTime<noteData.time) thisRenderer.enabled = true; else thisRenderer.enabled = false;
            }
            else
            {
                UpdateHoldSize();
                SetDirection();
                SetHoldPosition();
                if (LevelController.nowTime > noteData.time + noteData.holdTime) thisRenderer.enabled = false; else thisRenderer.enabled = true;
            }
            ///Auto
            //if (LevelController.nowTime > noteData.time) OnHit(HitACC.Perfect);
        }

        public virtual void OnNoteUpdate()
        {
        }

        public void UpdateHoldSize()
        {
            float sizeY;
            judgeline = transform.parent.GetComponent<JudgementLine>();
            noteData.time = Utils.GetTimeFromBeatTime(noteData.beatTime, judgeline.bpm);
            noteData.holdTime = Utils.GetTimeFromBeatTime(noteData.beatHoldTime, judgeline.bpm);
            //sizeY = (float)noteData.holdTime * speed; 
            sizeY = Mathf.Abs((float)(judgeline.GetSpeedDistance(noteData.time + noteData.holdTime) - judgeline.GetSpeedDistance(noteData.time)) * noteData.speed);
            if (judgeline.GetSpeedDistance(noteData.time) <= 0)
            {
                sizeY = Mathf.Abs((float)judgeline.GetSpeedDistance(noteData.time + noteData.holdTime)) * noteData.speed;
                //sizeY = sizeY-sizeY*(float)((LevelController.nowTime - noteData.time) / noteData.holdTime);//sizeY = (float)(judgeline.GetSpeedDistance(noteData.time + noteData.holdTime) * speed);
            }
            //if (sizeY != 0) holdHead.transform.localScale = new Vector3(1, 3 / sizeY, 1);
            sizeY /= 19f;
            transform.localScale = new Vector3(LevelController.noteSize, sizeY, 1f);
        }

        public void SetDirection()
        {
            if (direction == Direction.Below)
            {
                Vector3 localEulerAngles = new Vector3(0f, 0f, -180f);
                transform.localEulerAngles = localEulerAngles;
            }
        }
        public void SetHoldPosition()
        {
            if (judgeline.GetSpeedDistance(noteData.time) > 0) transform.localPosition = new Vector3(noteData.positionX, CalculateNotePosY(), 0f);
            else transform.localPosition = new Vector3(noteData.positionX, 0, 0f);
            //if (judgeline.GetSpeedDistance(noteData.time) > 0) transform.localPosition = new Vector3(noteData.positionX, (float)(noteData.time-LevelController.nowTime)*noteData.speed, 0f);
        }

        public virtual HitACC JudgeNote()
        {
            return HitACC.Perfect;
        }

        public static bool HLNote(int beatTime)
        {
            int count = 0;
            for (int i = 0; i < LevelController.instance.notes.Length; i++)
            {
                if (LevelController.instance.notes[i].noteData.beatTime == beatTime)
                {
                    count++;
                }
            }
            return count >= 2;
        }

        public int CompareTo(Note other)
        {
            if (noteData.time > other.noteData.time)
                return 1;
            if (noteData.time < other.noteData.time)
                return -1;
            else
                return 0;
        }

        [Serializable]
        public struct NoteData
        {
            public NoteType type;
            public double time;
            public float positionX;
            public float speed;
            public double holdTime;
            public int beatTime;
            public int beatHoldTime;
            public int index;
        }
        public enum Direction
        {
            Above, Below
        }
        public enum HitACC
        {
            Perfect, Good, Bad, Miss
        }
    }

    public enum NoteType
    {
        Click, Drag, Hold, Flick
    }
}